namespace ClassyBlog.DataAccess.Specs.Unit
{
    using System;
    using System.Linq;

    using Machine.Specifications;
    using NSubstitute;

    using DomainObjects;

    [Subject(typeof(PageRepository))]
    public class when_creating_page_repository_with_blank_data_directory
    {
        static Exception exception;

        Because of = () => exception =
            Catch.Exception(() =>
                new PageRepository(
                    string.Empty,
                    Substitute.For<IPathResolver>(),
                    Substitute.For<IArticleFactory<Page>>()));

        It should_throw_exception = () =>
            exception.ShouldBeOfType<ArgumentException>();
    }

    [Subject(typeof(PageRepository))]
    public class when_creating_page_repository_with_null_path_resolver
    {
        const string Path = @"c:\inetpub\wwwroot\classyblog\app_data";

        static Exception exception;

        Because of = () => exception =
            Catch.Exception(() => new PageRepository(
                Path, null, Substitute.For<IArticleFactory<Page>>()));

        It should_throw_exception = () =>
            exception.ShouldBeOfType<ArgumentNullException>();
    }

    [Subject(typeof(PageRepository))]
    public class when_creating_page_repository_with_null_factory
    {
        const string Path = @"c:\inetpub\wwwroot\classyblog\app_data";

        static Exception exception;

        Because of = () => exception =
            Catch.Exception(() => new PageRepository(
                Path, Substitute.For<IPathResolver>(), null));

        It should_throw_exception = () =>
            exception.ShouldBeOfType<ArgumentNullException>();
    }

    public abstract class PageRepositorySpec
    {
        protected const string DataDirectory =
            @"c:\inetpub\wwwroot\classyblog\app_data";

        protected static IPathResolver resolver;
        protected static IArticleFactory<Page> factory;
        protected static PageRepository respository;

        Establish context = () =>
        {
            resolver = Substitute.For<IPathResolver>();
            factory = Substitute.For<IArticleFactory<Page>>();

            respository = new PageRepository(DataDirectory, resolver, factory);
        };
    }

    [Subject(typeof(PageRepository))]
    public class when_finding_page : PageRepositorySpec
    {
        const string FileName = "about-us";

        static string dataPath;
        static string absoluteFilePath;

        Establish context = () =>
        {
            dataPath = DataDirectory + @"\pages";
            absoluteFilePath = dataPath + @"\" + FileName + ".md";
            resolver.Resolve(
                Arg.Any<string>(),
                Arg.Any<string>()).
                Returns(absoluteFilePath);

            factory.Create(
                Arg.Any<string>(),
                Arg.Any<string>()).
                Returns(ObjectMother.CreatePage());
        };

        Because of = () => respository.FindOne(FileName);

        It should_use_resolver_to_get_exact_file_location = () =>
            resolver.Received().Resolve(dataPath, FileName);

        It should_use_factory_to_create_page = () =>
            factory.Received().Create(dataPath, absoluteFilePath);
    }

    [Subject(typeof(PageRepository))]
    public class when_finding_non_existent_page : PageRepositorySpec
    {
        static Page page;

        Establish context = () => resolver.Resolve(
            Arg.Any<string>(), Arg.Any<string>()).Returns((string)null);

        Because of = () => page = respository.FindOne("foo-bar");

        It should_return_null = () => page.ShouldBeNull();
    }

    [Behaviors]
    public class NoFuturePublishablePagesResult
    {
        protected static IPathResolver resolver;
        protected static IArticleFactory<Page> factory;
        protected static string dataPath;
        protected static string absoluteFilePath;

        It should_use_resolver_to_get_exact_file_location = () =>
            resolver.Received().ResolveAll(dataPath, "help");

        It should_use_factory_to_create_page = () =>
            factory.Received().Create(dataPath, absoluteFilePath);
    }

    public abstract class NoFuturePublishableMultiplePagesRepositorySpec :
        PageRepositorySpec
    {
        protected const string FileName = "2010-04-02-17-30-00_about-us.md";

        protected static string dataPath;
        protected static string absoluteFilePath;

        Establish context = () =>
        {
            dataPath = DataDirectory + @"\pages";
            absoluteFilePath = dataPath + @"\help\" + FileName;
            resolver.ResolveAll(
                Arg.Any<string>(),
                Arg.Any<string>()).
                Returns(new[] { absoluteFilePath });

            factory.Create(
                Arg.Any<string>(),
                Arg.Any<string>()).
                Returns(ObjectMother.CreatePage());
        };
    }

    [Subject(typeof(PageRepository))]
    public class when_finding_pages_which_contains_no_future_publishable_page :
        NoFuturePublishableMultiplePagesRepositorySpec
    {
        Because of = () => respository.FindAll("help").ToList();

        Behaves_like<NoFuturePublishablePagesResult> pages;
    }

    [Subject(typeof(PageRepository))]
    public class when_counting_pages_which_contains_no_future_publishable_page :
        NoFuturePublishableMultiplePagesRepositorySpec
    {
        Because of = () => respository.Count("help");

        Behaves_like<NoFuturePublishablePagesResult> pages;
    }

    [Subject(typeof(PageRepository))]
    public class when_counting_page_of_a_null_page_repository
    {
        static PageRepository repository;
        static Exception exception;

        Establish context = () => repository = null;

        Because of = () => exception =
            Catch.Exception(() => repository.Count("some/page"));

        It should_throw_exception = () =>
            exception.ShouldBeOfType<ArgumentNullException>();
    }
}